package org.zoomdev.zoom.dao.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zoomdev.zoom.common.expression.Symbol;
import org.zoomdev.zoom.common.utils.Page;
import org.zoomdev.zoom.dao.*;
import org.zoomdev.zoom.dao.alias.NameAdapter;
import org.zoomdev.zoom.dao.modules.CoreDaoModule;
import org.zoomdev.zoom.dao.utils.DaoUtils;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

public final class ActiveRecord extends AbstractRecord implements ConnectionHolder, Ar {


    private static final Logger log = LoggerFactory.getLogger(CoreDaoModule.NAME);
    private final NameAdapter defaultNameAdapter;
    private final Dao dao;
    private NameAdapter nameAdapter;

    public ActiveRecord(Dao dao, NameAdapter nameAdapter, boolean output) {
        super(dao, new SimpleSqlBuilder(dao.getDriver()));
        this.nameAdapter = nameAdapter;
        this.defaultNameAdapter = nameAdapter;
        this.dao = dao;
    }

    @Override
    public Ar where(String key, Object value) {
        builder.where(key, value);
        return this;
    }

    @Override
    public Ar where(boolean condition, String key, Object value) {
        if (condition) {
            builder.where(key, value);
        }
        return this;
    }

    @Override
    public Ar where(String key, Symbol symbol, Object value) {
        builder.where(key, symbol, value);
        return this;
    }

    @Override
    public <E> Ar whereIn(String key, Collection<E> values) {
        builder.whereIn(key, values);
        return this;
    }

    @Override
    public Ar whereNotNull(String name) {
        builder.whereNotNull(name);
        return this;
    }

    @Override
    public Ar whereNull(String field) {
        builder.whereNull(field);
        return this;
    }

    @Override
    public Ar like(String name, SqlBuilder.Like like, Object value) {
        builder.like(name, like, value);
        return this;
    }

    @Override
    public Ar orWhere(String key, Object value) {
        builder.orWhere(key, value);
        return this;
    }

    @Override
    public Ar orLike(String name, SqlBuilder.Like like, Object value) {
        builder.orLike(name, like, value);
        return this;
    }

    @Override
    public Ar orderBy(String field, SqlBuilder.Sort sort) {
        builder.orderBy(field, sort);
        return this;
    }

    @Override
    public Ar groupBy(String field) {
        builder.groupBy(field);
        return this;
    }

    @Override
    public Ar having(String field, Symbol symbol, Object value) {
        builder.having(field, symbol, value);
        return this;
    }

    @Override
    public Ar union(SqlBuilder sqlBuilder) {
        throw new UnsupportedOperationException("未实现");
    }

    @Override
    public Ar unionAll(SqlBuilder sqlBuilder) {
        throw new UnsupportedOperationException("未实现");
    }

    @Override
    public Ar join(String table, String on) {
        this.join(table, on, SqlBuilder.INNER);
        return this;
    }

    @Override
    public Ar join(String table, String on, String type) {
        builder.join(table, on, type);
        return this;
    }

    @Override
    public Ar join(String table, String thisField, String otherField, SqlBuilder.JoinType type) {
        builder.join(table, thisField, otherField, type);
        return this;
    }

    @Override
    public Ar join(String table, String on, SqlBuilder.JoinType type) {
        return join(table, on, type.value());
    }

    @Override
    public Ar select(String select) {
        builder.select(select);
        return this;
    }

    @Override
    public Ar select(Iterable<String> select) {
        builder.select(select);
        return this;
    }

    @Override
    public Ar where(SqlBuilder.Condition condition) {
        builder.where(this, condition);
        return this;
    }

    @Override
    public Ar orWhere(SqlBuilder.Condition condition) {
        builder.orWhere(this, condition);
        return this;
    }

    @Override
    public Ar nameAdapter(NameAdapter nameAdapter) {
        this.nameAdapter = nameAdapter;
        return this;
    }

    @Override
    public List<Record> find() {
        builder.buildSelect();
        return query();
    }

    @Override
    public List<Record> limit(int position, int size) {
        builder.buildLimit(position, size);
        return query();
    }

    @Override
    public Page<Record> position(final int position, final int size) {

        return execute(connection -> {
            builder.buildLimit(position, size);
            List<Record> list = ActiveRecordUtils.executeQuery(
                    connection,
                    builder.sql(),
                    builder.values,
                    nameAdapter,
                    dao.isOutput()
            );
            builder.clear(false);
            remove2(builder.values);
            int total = _value(connection, DaoUtils.SELECT_COUNT, int.class);
            int page = DaoUtils.position2page(position, size);
            return new Page<>(list, page, size, total);
        });

    }

    @Override
    public Page<Record> page(int page, int size) {
        if (page <= 0)
            page = 1;
        return position((page - 1) * size, size);
    }

    @Override
    public List<Record> executeQuery(String sql, Object... args) {
        builder.sql.append(sql);
        Collections.addAll(builder.values, args);
        return query();
    }

    @Override
    public int update(Map<String, Object> data) {
        assert (data != null);
        builder.setAll(data);
        return update();
    }

    //===============================

    @Override
    public int update() {
        builder.buildUpdate();
        return executeUpdate();
    }

    @Override
    public Ar setAll(Map<String, Object> record) {
        builder.setAll(record);
        return this;
    }

    @Override
    public Ar set(String key, Object value) {
        builder.set(key, value);
        return this;
    }

    @Override
    public Ar setRaw(String key, Object value) {
        builder.setRaw(key, value);
        return this;
    }

    @Override
    public Record get() {
        builder.buildSelect();
        return execute(connection -> ActiveRecordUtils.executeGet(
                connection,
                builder.sql(),
                builder.values,
                nameAdapter,
                dao.isOutput()
        ));
    }

    @Override
    public int insert(Map<String, Object> data) {
        assert (data != null);
        builder.setAll(data);
        return insert();
    }

    @Override
    public int insert() {
        builder.buildInsert();
        return executeUpdate();
    }

    @Override
    public int delete() {
        builder.buildDelete();
        return executeUpdate();
    }

    @Override
    public int executeUpdate(String sql, Object... args) {
        builder.sql.append(sql);
        Collections.addAll(builder.values, args);
        return executeUpdate();
    }

    @Override
    public int execute(final String sql) {
        return execute(connection -> ActiveRecordUtils.execute(
                connection,
                sql,
                dao.isOutput()
        ));
    }

    @Override
    public Values values() {
        builder.buildSelect();
        return execute(connection -> ActiveRecordUtils.executeGetValues(
                connection,
                builder.sql(),
                builder.values,
                dao.isOutput()
        ));
    }

    @Override
    public Ar table(String table) {
        builder.table(table);
        return this;
    }

    @Override
    public int insertOrUpdate(String... keys) {
        builder.insertOrUpdate(keys);
        return executeUpdate();
    }

    @Override
    public Ar whereCondition(String key, Object... values) {
        builder.whereCondition(key, values);
        return this;
    }

    @Override
    public int insert(Record data, String... generateKeys) {
        builder.record.putAll(data);
        builder.buildInsert();
        return executeInsert(data, generateKeys);
    }

    public int executeInsert(final Record record, final String[] generatedKeys) {
        return execute(connection -> ActiveRecordUtils.executeInsert(
                connection,
                record,
                builder.sql(),
                builder.values,
                generatedKeys,
                dao.isOutput()
        ));
    }

    public int executeUpdate() {
        return execute(connection -> ActiveRecordUtils.executeUpdate(
                connection,
                builder.sql(),
                builder.values,
                dao.isOutput()
        ));
    }

    private List<Record> query() {
        return execute(connection -> ActiveRecordUtils.executeQuery(
                connection,
                builder.sql(),
                builder.values,
                nameAdapter,
                dao.isOutput()
        ));
    }

    @Override
    public <E> E value(final String key, final Class<E> typeOfE) {
        return execute(connection -> _value(connection, key, typeOfE));
    }

    @Override
    protected void clear() {
        super.clear();
        this.nameAdapter = this.defaultNameAdapter;
    }


}
